package com.gitee.wsl.model.plugin

import com.gitee.wsl.model.Model
import com.gitee.wsl.prop.AttributeKey
import com.gitee.wsl.prop.Attributes

internal val PLUGIN_INSTALLED_LIST = AttributeKey<Attributes>("ModelPluginRegistry")

interface ModelPlugin<TModel, out TConfig ,TPlugin> {
    /**
     * The [AttributeKey] for this plugin.
     */
     val key: AttributeKey<TPlugin>

    /**
     * Builds a [TPlugin] by calling the [block] with a [TConfig] config instance as receiver.
     */
     fun prepare(block: TConfig.() -> Unit = {}): TPlugin

    /**
     * Installs the [plugin] class for a [Model] defined at [scope].
     */
     fun install(plugin: TPlugin, scope: TModel)
}

/**
 * Returns a [plugin] installed in this client. Returns `null` if the plugin was not previously installed.
 */
 fun <M : Model<M>, B, F> M.pluginOrNull(plugin: ModelPlugin<M, B, F>): F? =
    attributes.getOrNull(PLUGIN_INSTALLED_LIST)?.getOrNull(plugin.key)

/**
 * Returns a [plugin] installed in [Model].
 *
 * @throws [IllegalStateException] if [plugin] is not installed.
 */
 fun <M : Model<M>, B, F> M.plugin(plugin: ModelPlugin<M, B, F>): F {
    return pluginOrNull(plugin) ?: throw IllegalStateException(
        "Plugin $plugin is not installed. Consider using `install(${plugin.key})` in client config first."
    )
}